#pragma once
#include "LinearAlgebra.h"
#include "AdvancedGraphicsViewer.h"
#include <list>

static int axis_compare = 0;

struct BVHTriangle {
	unsigned int indices[3];
	Vector3f centroid;
	unsigned int object_id;

	bool operator==(const BVHTriangle& t) {
		return centroid == t.centroid;
	}

	bool operator<(const BVHTriangle& t) {
		return centroid[axis_compare] < t.centroid[axis_compare];
	}
};

struct AABB {
	Vector3f min_corner;
	Vector3f max_corner;
};

struct BVHNode {
	AABB bounding_box;
	BVHTriangle* triangles;
	int num_triangles;

	BVHNode* left;
	BVHNode* right;
};

class BVH {
public:
	BVH() : m_root(NULL) {

	}

	vector<BVHTriangle> m_triangles;

	void clear() {
		clear(m_root);
	}

	void create(  vector<Model*>& ms) {
		clear();
		
		int n_indices = 0;
		
		for(unsigned int n = 0; n < ms.size(); n++)
		{
			n_indices += ms[n]->indices.size();
		}
		
		m_triangles.reserve( n_indices / 3 );
		m_triangles.resize( 0 );
		
		for(unsigned int n = 0; n < ms.size(); n++)
		{
			Model& m = *ms[n];
			for (unsigned int i=0; i<m.indices.size(); ) {
				BVHTriangle triangle;
				for (int j=0; j<3; j++) triangle.indices[j] = m.indices[i+j];
				
				triangle.object_id = 2 + n; // obj_id 0 = floor, 1 = light, 2..+ = models
				triangle.centroid = (m.vertices[m.indices[i++]].position + m.vertices[m.indices[i++]].position + m.vertices[m.indices[i++]].position) / 3.0f;
				m_triangles.push_back( triangle );
			}
		}

		m_root = split(ms, m_triangles.data(), m_triangles.size());
	}

	BVHNode* getRoot() { return m_root; }

private:

	AABB calculateAABB( vector<Model*>& ms, BVHTriangle* triangles, int num_triangles ) {
		AABB bb;
		bb.min_corner = makeVector3f( FLT_MAX, FLT_MAX, FLT_MAX );
		bb.max_corner = makeVector3f( -FLT_MAX, -FLT_MAX, -FLT_MAX );
		for (int t=0; t<num_triangles; t++) {
			BVHTriangle& triangle = triangles[t];
			for (int i=0; i<3; i++) {
				Vector3f vertex = ms[triangle.object_id-2]->vertices[ triangle.indices[i] ].position;
				for (int j=0; j<3; j++) {
					if (vertex[j] < bb.min_corner[j]) bb.min_corner[j] = vertex[j];
					if (vertex[j] > bb.max_corner[j]) bb.max_corner[j] = vertex[j];
				}
			}
		}
		return bb;
	}

	void clear(BVHNode* node) {
		if(node==NULL) return;
		clear(node->left);
		clear(node->right);
		delete node;
	}

	BVHNode* split( vector<Model*>& ms, BVHTriangle* triangles, int num_triangles);

	BVHNode* m_root;
};